#ifndef _AOI_IMPL_H_LX22
#define _AOI_IMPL_H_LX22

#include <cmath>
#include <unordered_map>
#include <set>
#include "aoi.h"
#include "bitmap.h"

struct aoi_node_t {
	aoi_node_t * p_x_prev;
	aoi_node_t * p_x_next;

	aoi_node_t * p_z_prev;
	aoi_node_t * p_z_next;

	float x, y, z; // 节点当前坐标 
	float aoi_x, aoi_y, aoi_z; // aoi 坐标, 用于 aoi 判断, 只有节点当前坐标与此坐标偏差超过 aoi_deviation 时才更新此坐标 
	float aoi_range;
	float aoi_range_square;

	engine_id_t engine_id;

	Bitmap* bitmap;
	int* aoi_calc_filter;

	aoi_node_t() : p_x_prev(NULL), p_x_next(NULL), p_z_prev(NULL), p_z_next(NULL), engine_id(INVALID_ENGINE_ID), bitmap(NULL), aoi_calc_filter(NULL) {}

	aoi_node_t(engine_id_t engine_id, float x, float y, float z, float aoi_range, int max_node_num);
	~aoi_node_t();
};

class aoi_mgr_t {
public:
	aoi_mgr_t(void * p_user_data, cb_a_enter_aoi_of_b a_enter_aoi_of_b, cb_a_leave_aoi_of_b a_leave_aoi_of_b, cb_a_batch_enter_leave_aoi a_batch_enter_leave_aoi, float aoi_deviation, bool enable_max_aoi_range_check);
	~aoi_mgr_t();

	void * get_user_data() { return p_user_data; };

	void set_max_node_num(int num);

	int add(engine_id_t engine_id, float x, float y, float z, float aoi_range);
	int remove(engine_id_t engine_id);

	int move_to(engine_id_t engine_id, float new_x, float new_y, float new_z);

	int set_node_aoi_range(engine_id_t engine_id, float new_aoi_range);

	int set_aoi_deviation(float new_aoi_deviation);

	int get_node_info(engine_id_t engine_id, float * p_aoi_range, float * p_x, float * p_y, float * p_z, float * p_aoi_x, float * p_aoi_y, float * p_aoi_z);

	int iterate_this_see(engine_id_t engine_id, cb_aoi_iterate iterate_func);

	int iterate_see_this(engine_id_t engine_id, cb_aoi_iterate iterate_func);

	int get_see_rel(engine_id_t a, engine_id_t b);

	void set_debug_mode(int mode) { debug_mode = mode; }

	aoi_node_t* get_node_with_engine_id(engine_id_t engine_id);

	void validate();

private:
	float get_aoi_distance_square(const aoi_node_t * p_a, const aoi_node_t * p_b)
	{
		float x_diff = p_a->aoi_x - p_b->aoi_x;
		float z_diff = p_a->aoi_z - p_b->aoi_z;
		return x_diff * x_diff + z_diff * z_diff;
	}

	bool axis_distance_in_range(const aoi_node_t * p_a, const aoi_node_t * p_b, const float range)
	{
		if (range <= 0.f)
			return false;
		float x_distance = std::abs(p_a->aoi_x - p_b->aoi_x);
		float z_distance = std::abs(p_a->aoi_z - p_b->aoi_z);
		return range >= x_distance && range >= z_distance;
	}

	int do_get_see_rel(const aoi_node_t * p_this, const aoi_node_t * p_other)
	{
		// 根据位置计算理论视野关系
		int rel = 0;

		float x_distance = std::abs(p_this->aoi_x - p_other->aoi_x);
		float z_distance = std::abs(p_this->aoi_z - p_other->aoi_z);
		float distance_square = -1.f;

		if (p_this->aoi_range > 0.f && p_this->aoi_range >= x_distance && p_this->aoi_range >= z_distance)
		{
			rel |= AOI_REL_InAxisRange;
			rel |= AOI_REL_ThisCanSeeOther;
		}
		return rel;
	}

	int check_rel_on_add(const aoi_node_t * p_this, const aoi_node_t * p_other)
	{
		int enter_rel = 0;
		int rel = do_get_see_rel(p_this, p_other);
		if (rel & AOI_REL_OtherCanSeeThis)
			enter_rel |= AOI_REL_ThisEnterOtherAOI;
		if (rel & AOI_REL_ThisCanSeeOther)
			enter_rel |= AOI_REL_OtherEnterThisAOI;
		return enter_rel;
	}

	int check_rel_on_del(const aoi_node_t * p_this, const aoi_node_t * p_other)
	{
		int leave_rel = 0;
		int rel = do_get_see_rel(p_this, p_other);
		if (rel & AOI_REL_OtherCanSeeThis)
			leave_rel |= AOI_REL_ThisLeaveOtherAOI;
		if (rel & AOI_REL_ThisCanSeeOther)
			leave_rel |= AOI_REL_OtherLeaveThisAOI;
		return leave_rel;
	}

	int check_rel_on_move(const aoi_node_t * p_new, const aoi_node_t * p_other);

	aoi_node_t * alloc_aoi_node(engine_id_t engine_id, float x, float y, float z, float aoi_range);
	void free_aoi_node(aoi_node_t * p_node);

	void update_max_aoi_range();

	void * p_user_data;

	aoi_node_t * p_x_head;
	aoi_node_t * p_z_head;

	cb_a_enter_aoi_of_b a_enter_aoi_of_b;
	cb_a_leave_aoi_of_b a_leave_aoi_of_b;
	cb_a_batch_enter_leave_aoi a_batch_enter_leave_aoi;

	int max_node_num;
	float aoi_deviation; // 节点当前坐标和 aoi 坐标的最大偏差 
	float max_aoi_range; // 记录最大 aoi 范围, 用于缩小搜索范围 

	int debug_mode;

	typedef std::unordered_map<engine_id_t, aoi_node_t*> aoi_nodes_table_t;
	aoi_nodes_table_t aoi_nodes_table;
};

#endif
